// =========================
// SFINAE 基本概念
// =========================

SFINAE(Substitution Failure Is Not An Error, 替换失败不是错误)是C++模板编程中的一个重要原则。它指的是在模板实例化过程中, 如果替换模板参数导致无效代码, 编译器不会报错, 而是简单地丢弃该模板特化, 继续寻找其他可能的匹配。
这一原则为我们提供了一种强大的技术, 可以在编译期检测类型特性, 并基于这些特性选择不同的实现路径。

// =========================
// 19.4.1 SFINAE技术的基础
// =========================
SFINAE最初主要用于函数模板的重载解析。例如: 
// 针对指针类型的重载
template<typename T>
typename T::value_type test(T const&);

// 针对非指针类型的备选重载
template<typename>
void test(...);

// 使用示例
void example() {
    test(42);        // 使用第二个重载, 因为int没有value_type
    test(std::vector<int>());  // 使用第一个重载, 因为vector有value_type
}


// ========================
// 19.4.2 SFINAE与类型特征
// ========================
SFINAE可以用来创建类型特征, 检测类型是否具有特定属性: 
// 检测类型是否有value_type成员
template<typename T, typename = void>
struct HasValueType : std::false_type {};

template<typename T>
struct HasValueType<T, std::void_t<typename T::value_type>> : std::true_type {};

这里使用了std::void_t(C++17引入)作为SFINAE上下文, 如果T::value_type无效, 特化版本会被丢弃, 使用主模板。


// =======================
// 19.4.3 检测成员函数和操作符
// =======================
SFINAE可以检测类型是否有特定成员函数或支持特定操作: 

// 检测是否有size()方法
template<typename T, typename = void>
struct HasSizeMethod : std::false_type {};

template<typename T>
struct HasSizeMethod<T, 
    std::void_t<decltype(std::declval<T>().size())>> : std::true_type {};

// 检测是否支持小于比较
template<typename T, typename = void>
struct HasLessThan : std::false_type {};

template<typename T>
struct HasLessThan<T, 
    std::void_t<decltype(std::declval<T>() < std::declval<T>())>> : std::true_type {};

// ======================
// 19.4.4 检测表达式有效性
// ======================
SFINAE不仅可以检测成员存在性, 还可以检测任意表达式的有效性: 
// 检测类型是否可以加法操作
template<typename T, typename U, typename = void>
struct IsAddable : std::false_type {};

template<typename T, typename U>
struct IsAddable<T, U, 
    std::void_t<decltype(std::declval<T>() + std::declval<U>())>> : std::true_type {};


// =======================
// 19.4.5 SFINAE与返回类型
// =======================
SFINAE可以用于根据类型特性选择不同的返回类型: 
// 对容器类型返回其value_type
template<typename T>
auto getValueType(T const& t) -> typename T::value_type {
    return *t.begin();
}

// 对非容器类型返回其本身
template<typename T>
auto getValueType(T const& t) -> T {
    return t;
}


// =======================
// 19.4.6 SFINAE的高级应用
// =======================
// 检测嵌套类型
// 检测是否有iterator_category嵌套类型
template<typename T, typename = void>
struct HasIteratorCategory : std::false_type {};

template<typename T>
struct HasIteratorCategory<T, 
    std::void_t<typename T::iterator_category>> : std::true_type {};

// 检测类型关系
// 检测T是否可以转换为U
template<typename T, typename U, typename = void>
struct IsConvertibleTo : std::false_type {};

template<typename T, typename U>
struct IsConvertibleTo<T, U, 
    std::void_t<decltype(static_cast<U>(std::declval<T>()))>> : std::true_type {};

// 检测复杂条件
// 检测是否是随机访问迭代器
template<typename T, typename = void>
struct IsRandomAccessIterator : std::false_type {};

template<typename T>
struct IsRandomAccessIterator<T, std::void_t<
    decltype(std::declval<T>() + 1),                // 支持+操作
    decltype(std::declval<T>() - std::declval<T>()), // 支持-操作
    decltype(std::declval<T>()[0])                  // 支持[]操作
>> : std::true_type {};

// =================================
// 19.4.7 SFINAE的实际应用
// =================================
// 条件编译
template<typename T>
void process(T const& value) {
    if constexpr (HasToString<T>::value) {
        std::cout << value.toString();
    } else {
        std::cout << value;
    }
}

// 启用/禁用函数重载
// 只对支持比较的类型启用
template<typename T>
std::enable_if_t<HasLessThan<T>::value, bool>
isSorted(std::vector<T> const& v) {
    // 实现排序检查
    return true;
}

// 对不支持比较的类型提供编译错误
template<typename T>
std::enable_if_t<!HasLessThan<T>::value, bool>
isSorted(std::vector<T> const&) {
    static_assert(HasLessThan<T>::value, "Type must support operator<");
    return false;
}

// 19.4.8 C++17与C++20中的改进
// C++17: std::void_t
// C++17标准库引入了std::void_t, 简化了SFINAE表达式: 
template<typename... Ts> struct make_void { using type = void; };
template<typename... Ts> using void_t = typename make_void<Ts...>::type;

// C++20: Concepts
C++20引入了Concepts, 提供了比SFINAE更清晰、更强大的类型约束机制: 
template<typename T>
concept Addable = requires(T a, T b) {
    { a + b } -> std::convertible_to<T>;
};

template<Addable T>
T add(T a, T b) {
    return a + b;
}
